/*
 * Copyright 2011 woozzu
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.nd.android.musicboxlibrary.ui.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.widget.Adapter;
import android.widget.ListView;
import android.widget.SectionIndexer;

import com.nd.android.musicboxlibrary.R;
import com.nd.android.musicboxlibrary.business.comm.MagicNumber;

/**
 * 
 * <br>
 * Created 2014年7月17日 上午1:24:37
 * 
 * @version
 * @author wat.ztag
 * 
 * @see
 */
public class IndexScroller implements MagicNumber {
    
    /**
     * 
     * <br>Created 2014年11月28日 上午12:05:46
     * @author       wat.ztag
    */
    @Override
    public void initMagicNumber() {}

    private float mIndexbarWidth;
    private float mIndexbarMargin;
    private float mPreviewPadding;
    private float mDensity;
    private float mScaledDensity;
    private float mAlphaRate;
    private int mState = STATE_HIDDEN;
    private int mListViewWidth;
    private int mListViewHeight;
    private int mCurrentSection = -1;
    private boolean mIsIndexing = false;
    private ListView mListView = null;
    private SectionIndexer mIndexer = null;
    private String[] mSections = null;
    private RectF mIndexbarRect;

    private Context mContext;

    private static final int STATE_HIDDEN = 0;
    private static final int STATE_SHOWING = 1;
    private static final int STATE_SHOWN = 2;
    private static final int STATE_HIDING = 3;

    /**
     * Creates a new instance of IndexScroller. <br>
     * Created 2014年9月15日 下午11:03:19
     * 
     * @param context Context
     * @param lv ListView
     */
    public IndexScroller(Context context, ListView lv) {
        this.mContext = context;
        mDensity = context.getResources().getDisplayMetrics().density;
        mScaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
        mListView = lv;
        setAdapter(mListView.getAdapter());

        mIndexbarWidth = n20 * mDensity;
        mIndexbarMargin = n5 * mDensity;
        mPreviewPadding = n3 * mDensity;
    }

    /**
     * 
     * <br>
     * Created 2014年9月15日 下午11:03:23
     * 
     * @param canvas Canvas
     * @author wat.ztag
     */
    public void draw(Canvas canvas) {
        if (mState == STATE_HIDDEN) {
            return;
        }

        // mAlphaRate determines the rate of opacity
        // Paint indexbarPaint = new Paint();
        // indexbarPaint.setColor(Color.BLACK);
        // indexbarPaint.setAlpha((int) (64 * mAlphaRate));
        // indexbarPaint.setAntiAlias(true);
        // canvas.drawRoundRect(mIndexbarRect, 5 * mDensity, 5 * mDensity, indexbarPaint);

        if (mSections != null && mSections.length > 0) {
            // Preview is shown when mCurrentSection is set
            if (mCurrentSection >= 0) {
                Paint previewPaint = new Paint();
                previewPaint.setColor(Color.BLACK);
                previewPaint.setAlpha(n96);
                previewPaint.setAntiAlias(true);
                previewPaint.setShadowLayer(n3, 0, 0, Color.argb(n64, 0, 0, 0));

                Paint previewTextPaint = new Paint();
                previewTextPaint.setColor(Color.WHITE);
                previewTextPaint.setAntiAlias(true);
                previewTextPaint.setTextSize(n50 * mScaledDensity);

                float previewTextWidth = previewTextPaint.measureText(mSections[mCurrentSection]);
                float previewSize = 2 * mPreviewPadding + previewTextPaint.descent()
                        - previewTextPaint.ascent();
                RectF previewRect = new RectF((mListViewWidth - previewSize) / 2,
                        (mListViewHeight - previewSize) / 2, (mListViewWidth - previewSize) / 2
                                + previewSize, (mListViewHeight - previewSize) / 2 + previewSize);

                canvas.drawRoundRect(previewRect, n5 * mDensity, n5 * mDensity, previewPaint);
                canvas.drawText(mSections[mCurrentSection], previewRect.left
                        + (previewSize - previewTextWidth) / 2 - 1, previewRect.top
                        + mPreviewPadding - previewTextPaint.ascent() + 1, previewTextPaint);
            }

            Paint indexPaint = new Paint();
            indexPaint.setColor(mContext.getResources().getColor(R.color.mbx_jay_deep_blue));
            indexPaint.setAlpha((int) (n255 * mAlphaRate));
            indexPaint.setAntiAlias(true);
            indexPaint.setTextSize(n12 * mScaledDensity);

            float sectionHeight = (mIndexbarRect.height() - 2 * mIndexbarMargin) / mSections.length;
            float paddingTop = (sectionHeight - (indexPaint.descent() - indexPaint.ascent())) / 2;
            for (int i = 0; i < mSections.length; i++) {
                float paddingLeft = (mIndexbarWidth - indexPaint.measureText(mSections[i])) / 2;
                canvas.drawText(mSections[i], mIndexbarRect.left + paddingLeft, mIndexbarRect.top
                        + mIndexbarMargin + sectionHeight * i + paddingTop - indexPaint.ascent(),
                        indexPaint);
            }
        }
    }

    /**
     * 
     * <br>
     * Created 2014年9月15日 下午11:03:28
     * 
     * @param ev MotionEvent
     * @return boolean
     * @author wat.ztag
     */
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // If down event occurs inside index bar region, start indexing
            if (mState != STATE_HIDDEN && contains(ev.getX(), ev.getY())) {
                setState(STATE_SHOWN);

                // It demonstrates that the motion event started from index bar
                mIsIndexing = true;
                // Determine which section the point is in, and move the list to that section
                mCurrentSection = getSectionByPoint(ev.getY());
                mListView.setSelection(mIndexer.getPositionForSection(mCurrentSection));
                return true;
            }
            break;
        case MotionEvent.ACTION_MOVE:
            if (mIsIndexing) {
                // If this event moves inside index bar
                if (contains(ev.getX(), ev.getY())) {
                    // Determine which section the point is in, and move the list to that section
                    mCurrentSection = getSectionByPoint(ev.getY());
                    mListView.setSelection(mIndexer.getPositionForSection(mCurrentSection));
                }
                return true;
            }
            break;
        case MotionEvent.ACTION_UP:
            if (mIsIndexing) {
                mIsIndexing = false;
                mCurrentSection = -1;
            }
            if (mState == STATE_SHOWN) {
                setState(STATE_HIDING);
            }
            break;
        default:
            break;
        }
        return false;
    }

    /**
     * 
     * <br>
     * Created 2014年9月15日 下午11:03:36
     * 
     * @param w int
     * @param h int
     * @param oldw int
     * @param oldh int
     * @author wat.ztag
     */
    public void onSizeChanged(int w, int h, int oldw, int oldh) {
        mListViewWidth = w;
        mListViewHeight = h;
        mIndexbarRect = new RectF(w - mIndexbarMargin - mIndexbarWidth, mIndexbarMargin, w
                - mIndexbarMargin, h - mIndexbarMargin);
    }

    /**
     * 
     * <br>
     * Created 2014年9月15日 下午11:03:40
     * 
     * @author wat.ztag
     */
    public void show() {
        if (mState == STATE_HIDDEN) {
            setState(STATE_SHOWING);
        } else if (mState == STATE_HIDING) {
            setState(STATE_HIDING);
        }
    }

    /**
     * 
     * <br>
     * Created 2014年9月15日 下午11:03:44
     * 
     * @author wat.ztag
     */
    public void hide() {
        if (mState == STATE_SHOWN) {
            setState(STATE_HIDING);
        }
    }

    /**
     * 
     * <br>
     * Created 2014年9月15日 下午11:03:48
     * 
     * @param adapter Adapter
     * @author wat.ztag
     */
    public void setAdapter(Adapter adapter) {
        if (adapter instanceof SectionIndexer) {
            mIndexer = (SectionIndexer) adapter;
            mSections = (String[]) mIndexer.getSections();
        }
    }

    /**
     * 
     * <br>
     * Created 2014年9月15日 下午11:03:52
     * 
     * @param state int
     * @author wat.ztag
     */
    private void setState(int state) {
        if (state < STATE_HIDDEN || state > STATE_HIDING) {
            return;
        }

        mState = state;
        switch (mState) {
        case STATE_HIDDEN:
            // Cancel any fade effect
            mHandler.removeMessages(0);
            break;
        case STATE_SHOWING:
            // Start to fade in
            mAlphaRate = 0;
            fade(0);
            break;
        case STATE_SHOWN:
            // Cancel any fade effect
            mHandler.removeMessages(0);
            break;
        case STATE_HIDING:
            // Start to fade out after three seconds
            mAlphaRate = 1;
            fade(n3000);
            break;
        default:
            break;
        }
    }

    /**
     * 
     * <br>
     * Created 2014年9月15日 下午11:03:57
     * 
     * @param x float
     * @param y float
     * @return boolean
     * @author wat.ztag
     */
    private boolean contains(float x, float y) {
        // Determine if the point is in index bar region, which includes the right margin of the bar
        return (x >= mIndexbarRect.left && y >= mIndexbarRect.top && y <= mIndexbarRect.top
                + mIndexbarRect.height());
    }

    /**
     * 
     * <br>
     * Created 2014年9月15日 下午11:04:01
     * 
     * @param y float
     * @return int
     * @author wat.ztag
     */
    private int getSectionByPoint(float y) {
        if (mSections == null || mSections.length == 0) {
            return 0;
        }
        if (y < mIndexbarRect.top + mIndexbarMargin) {
            return 0;
        }
        if (y >= mIndexbarRect.top + mIndexbarRect.height() - mIndexbarMargin) {
            return mSections.length - 1;
        }
        return (int) ((y - mIndexbarRect.top - mIndexbarMargin) / 
                ((mIndexbarRect.height() - 2 * mIndexbarMargin) / mSections.length));
    }

    /**
     * 
     * <br>
     * Created 2014年9月15日 下午11:04:07
     * 
     * @param delay long
     * @author wat.ztag
     */
    private void fade(long delay) {
        mHandler.removeMessages(0);
        mHandler.sendEmptyMessageAtTime(0, SystemClock.uptimeMillis() + delay);
    }

    private Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (mState) {
            case STATE_SHOWING:
                // Fade in effect
                mAlphaRate += (1 - mAlphaRate) * f02;
                if (mAlphaRate > f09) {
                    mAlphaRate = 1;
                    setState(STATE_SHOWN);
                }

                mListView.invalidate();
                fade(n10);
                break;
            case STATE_SHOWN:
                // If no action, hide automatically
                setState(STATE_HIDING);
                break;
            case STATE_HIDING:
                // Fade out effect
                mAlphaRate -= mAlphaRate * f02;
                if (mAlphaRate < f01) {
                    mAlphaRate = 0;
                    setState(STATE_HIDDEN);
                }

                mListView.invalidate();
                fade(n10);
                break;
            default:
                break;
            }
        }

    };
}
